#include "Err.h"

#include <stdarg.h>
#include <string.h>
#include <fan-llvm/core/Core.h>
#include "Env.h"
#include "Str.h"
#include "Type.h"
#include "UnsupportedErr.h"


Type* Err_classType = NULL;
VTable* Err_classVTable = NULL;


/**********************************************
 *
 * Methods and fields
 *
 **********************************************/


/* Void make(Str msg, Err? cause) */
void Err_make_0(Err* self)
{
  Err_make(self, Str_defVal(), NULL);
}


/* Void make(Str msg, Err? cause) */
void Err_make_1(Err* self, Str* msg)
{
  Err_make(self, msg, NULL);
}


/* Void make(Str msg, Err? cause) */
void Err_make(Err* self, Str* msg, Err* cause)
{
  Obj_make(VAL_MAKE_REF(self));
  self->msg = msg;
  self->cause = cause;
}


/* Str toStr() */
Str* Err_toStr(Obj self)
{
  Err* err = VAL_GET_REF(self);
  Str* qname = Type_qname(Obj_typeof(self));
  if (Str_size(err->msg) == 0)
    return qname;
  else
    return Str_newWithFormat("%r: %r", qname, err->msg);
}


/* Str msg() */
Str* Err_msg(Err* self)
{
  return self->msg;
}


/* Err? cause() */
Err* Err_cause(Err* self)
{
  return self->cause;
}


/* This trace(OutStream out, [Str:Obj]? options) */
Err* Err_trace_0(Err* self)
{
  return Err_trace(self, Env_out(Env_cur()), NULL);
}


/* This trace(OutStream out, [Str:Obj]? options) */
Err* Err_trace_1(Err* self, OutStream* out)
{
  return Err_trace(self, out, NULL);
}


/* This trace(OutStream out, [Str:Obj]? options) */
Err* Err_trace(Err* self, OutStream* out, Map* options)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Err.trace not implemented yet"));
  return NULL;
}


/* Str traceToStr() */
Str* Err_traceToStr(Err* self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Err.traceToStr not implemented yet"));
  return NULL;
}


/**********************************************
 *
 * Extra methods
 *
 **********************************************/


/* Create a new Err instance and initialize it with Err_make() */
Err* Err_new(Str* msg, Err* cause)
{
  Err* err = (Err*)Obj_allocate(sizeof(Err), Err_classVTable);
  Err_make(err, msg, cause);
  return err;
}


/* Create a new Err instance and initialize it with the message given by the specified format.
 * 'msgFormat' uses the same format as Str_newWithFormat() */
Err* Err_newWithFormattedMsg(const char* msgFormat, ...)
{
  va_list args;
  va_start(args, msgFormat);
  Str* msg = Str_newWithFormatValist(msgFormat, args);
  va_end(args);
  return Err_new(msg, NULL);
}


/**********************************************
 *
 * Type-system functions
 *
 **********************************************/


Type* Err_initType(void)
{
  if (Err_classType == NULL)
  {
    Type* baseType = Obj_initType();
    VTable* vtable = Err_initVTable();
    Type* type = Type_new("Err", baseType, vtable, sizeof(Err), sizeof(ErrVTable));
    Type_registerCtorImplementation(type, "make", Err_make_0, 0);
    Type_registerCtorImplementation(type, "make", Err_make_1, 1);
    Type_registerCtorImplementation(type, "make", Err_make, 2);
    Type_registerMethodImplementation(type, "cause", Err_cause, 0);
    Type_registerMethodImplementation(type, "msg", Err_msg, 0);
    Type_registerMethodImplementation(type, "toStr", Err_toStr, 0);
    Type_registerMethodImplementation(type, "trace", Err_trace_0, 0);
    Type_registerMethodImplementation(type, "trace", Err_trace_1, 1);
    Type_registerMethodImplementation(type, "trace", Err_trace, 2);
    Type_registerMethodImplementation(type, "traceToStr", Err_traceToStr, 0);
    Err_classType = type;
    Err_initStatic();
  }
  return Err_classType;
}


VTable* Err_initVTable(void)
{
  if (Err_classVTable == NULL)
  {
    VTable* vtable = memoryAlloc(sizeof(ErrVTable));
    memcpy(vtable, Obj_initVTable(), sizeof(FanObjVTable));
    ((FanObjVTable*)vtable)->toStr = Err_toStr;
    Err_classVTable = vtable;
  }
  return Err_classVTable;
}


void Err_initStatic(void)
{
}
